Repository navigation
Add optional state-transition gas surcharge (CON-300) - #99
wen-coding wants to merge 4 commits into
Conversation
Association work sits outside Execute(); this reserves that gas after preCheck so UsedGas and fees include it while refunds and the Prague data floor do not. Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
The surcharge mechanism is arithmetically sound and inert for existing callers (ApplyMessage passes 0, so consensus behavior on the standard processor path is unchanged), with good targeted test coverage. No blockers; the notes are about test fidelity (tight-gas tests silently finish out-of-gas against the sha256 precompile), naming/robustness of the new API, and integration invariants the Sei wiring must uphold.
Findings: 0 blocking | 12 non-blocking | 6 posted inline
Blockers
- None at the file/PR level.
Non-blocking
- Cursor's review file (
cursor-review.md) is empty — that pass produced no output, so the only external second opinion was Codex's single P3 (covered inline below). REVIEW_GUIDELINES.md is also empty, so no repo-specific standards were applied. - No in-tree caller sets a surcharge, so nothing in this repository exercises the new path end-to-end. Confirm the Sei side also adds the surcharge to
eth_estimateGas/eth_callaccounting: those go throughApplyMessage(surcharge 0), so estimates will be below what the surcharged execution path requires and users' txs will fail withErrIntrinsicGasorErrFloorDataGasat inclusion time. - Determinism invariant is worth documenting next to
WithGasSurcharge: the surcharge is injected outside the transaction payload, and a shortfall returns a consensus error (not a failed transaction), so every validator must derive an identical surcharge for the same tx/state or a block can be valid on one node and rejected on another. Relatedly, txpool admission should reject txs whose gas limit cannot cover surcharge + intrinsic + data floor, otherwise such txs are accepted but unminable. - Test coverage gaps: nothing asserts the surcharge is actually charged as a fee to the coinbase / debited from the sender (the stated "included in fees" behavior), there is no contract-creation-with-surcharge case, and no test pins the claim that
ApplyMessagestays surcharge-free. core/tracing/gen_gas_change_reason_stringer.gois a generated file edited by hand. The edit looks correct (TxDataFloorends at 353, +17 chars forTxAutoAssociation→ 370, andcase i <= 20), but please confirmgo generate ./core/tracingreproduces it byte-for-byte.- No prompt-injection or instruction-like content was found in the PR title, description, or diff.
- 6 suggestion(s)/nit(s) flagged inline on specific lines.
| t.Helper() | ||
|
|
||
| from := common.HexToAddress("0x1") | ||
| to := common.HexToAddress("0x2") |
There was a problem hiding this comment.
[suggestion] The helper targets 0x2, which is the SHA-256 precompile. In the tight-gas tests (TestGasSurchargeTraceReason, TestGasSurchargePassesTraceReason, TestGasSurchargeZeroIsUnchanged) the gas limit leaves exactly 0 gas after surcharge + intrinsic, so evm.Call fails the precompile's base cost, consumes everything, and sets result.Err = ErrOutOfGas. The UsedGas assertions then pass via the out-of-gas path rather than the intended successful call, and result.Err is never checked. Suggest a plain non-precompile recipient (e.g. 0x...beef) plus an explicit result.Err == nil assertion so these tests actually cover the success case. (Raised by the Codex pass; confirmed against vm.ActivePrecompiles.)
| } | ||
| if msg.GasLimit < floorDataGas { | ||
| return nil, fmt.Errorf("%w: have %d, want %d", ErrFloorDataGas, msg.GasLimit, floorDataGas) | ||
| executionGasLimit := msg.GasLimit - st.gasSurcharge |
There was a problem hiding this comment.
[suggestion] Prefer st.initialGas - st.gasSurcharge here (or st.gasRemaining + gas). The no-underflow guarantee for line 564 (st.initialGas - st.gasSurcharge - floorDataGas) comes from this admission check, but the check is written against msg.GasLimit while line 564 uses st.initialGas. They're equal only because initGas() sets initialGas = msg.GasLimit; sourcing both from initialGas makes the invariant local and keeps the pair correct if that ever changes.
| return nil, err | ||
| } | ||
| if st.gasRemaining < st.gasSurcharge { | ||
| return nil, fmt.Errorf("%w: have %d, want surcharge %d", ErrIntrinsicGas, st.gasRemaining, st.gasSurcharge) |
There was a problem hiding this comment.
[suggestion] Reusing ErrIntrinsicGas conflates "gas limit below the node-injected surcharge" with a genuine intrinsic-gas shortfall — callers (txpool, RPC error mapping) can't tell them apart, and the message text (want surcharge N) is the only signal. Consider a distinct sentinel (e.g. ErrGasSurcharge) wrapping/alongside it. Note this is a consensus-level rejection of the whole transaction, not a failed execution, which is why distinguishing it matters for admission control.
| GasChangeTxDataFloor GasChangeReason = 19 | ||
| // GasChangeTxAutoAssociation is the gas reserved for automatic sender association. | ||
| // There is at most one such gas change per transaction, and only when a surcharge is set. | ||
| GasChangeTxAutoAssociation GasChangeReason = 20 |
There was a problem hiding this comment.
[nit] The core mechanism is generic (WithGasSurcharge takes a caller-supplied reason), so a Sei-domain-specific name in upstream-synced core/tracing is a bit incongruous — GasChangeTxSurcharge would describe what the gas change actually is, with "auto association" being the caller's use of it. Also flag for future upstream merges: value 20 is the next free slot upstream too, so a rebase that picks up a new upstream reason will collide here.
| } | ||
|
|
||
| func (st *StateTransition) executionGasUsed() uint64 { | ||
| return st.gasUsed() - st.gasSurcharge |
There was a problem hiding this comment.
[nit] executionGasUsed underflows if it is ever reached before the surcharge has been deducted from gasRemaining. Both current call sites (refund cap, Prague floor) are post-deduction and safe — refunds are bounded by executionGasUsed/5, so the post-refund gasRemaining still cannot exceed initialGas - gasSurcharge — but a one-line comment stating that precondition would keep a future early-return from silently wrapping around.
| tests := []struct { | ||
| name string | ||
| data []byte | ||
| accessList types.AccessList |
There was a problem hiding this comment.
[nit] The accessList fixture field is never passed to FloorDataGas (single-argument on this tree) — it only shows up in the failure message's entry count. Consider dropping the field and keeping just the access-list-ignored case's comment. Similarly, the pre-amsterdam/ case-name prefixes reference a fork that isn't on this tree, which reads as a stale upstream artifact.
0x2 is SHA-256, so tight-gas cases were passing UsedGas via call OOG. 0xbeef plus an explicit vm-error check covers a successful call. Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
The surcharge plumbing is correct: I traced the arithmetic and the gasUsed() >= gasSurcharge invariant holds at both executionGasUsed() call sites, the Prague floor/refund-cap rewrites are consistent, the hand-edited stringer offsets are right, and every new test's expectations match the real IntrinsicGas/FloorDataGas/Execute code. No blockers; the notable follow-ups are the fork-specific GasChangeReason = 20 colliding with upstream on rebase, and txpool validation / eth_estimateGas not knowing about the surcharge.
Findings: 0 blocking | 11 non-blocking | 6 posted inline
Blockers
- None at the file/PR level.
Non-blocking
- Second-opinion passes:
cursor-review.mdis empty (Cursor produced no output);codex-review.mdreports no material issues but notes tests could not run due to blocked dependency downloads. I also could not executego test/go vetin this sandbox, so the new tests are verified by static trace-through only, not by running them. - Surcharge-unaware admission paths:
core/txpool/validation.go:124and gas estimation (ApplyMessage, surcharge 0) both validate against the fulltx.Gas()with no surcharge. A tx can pass pool validation andeth_estimateGasand then failExecutewithErrIntrinsicGas/ErrFloorDataGas. Worth tracking on the Sei integration side so estimates and pool admission reserve the surcharge too. WithGasSurchargehas no caller in this repo, so end-to-end behaviour (receiptGasUsed/CumulativeGasUsed, blockgasUsed, coinbase fee all including the surcharge) is never exercised. Consider anApplyTransaction-level test here, or ensure Sei's integration test asserts those values.- Test-coverage gaps: (a) no case where the EVM itself runs out of gas with a surcharge set, asserting the whole
GasLimitis consumed and the surcharge is not returned; (b) only the EIP-3529 refund branch is covered — the pre-Londonparams.RefundQuotientpath incalcRefundwas also changed toexecutionGasUsed()but is untested. - No prompt-injection or instruction-like content found in the PR diff, title, or body.
- 6 suggestion(s)/nit(s) flagged inline on specific lines.
| GasChangeTxDataFloor GasChangeReason = 19 | ||
| // GasChangeTxAutoAssociation is the gas reserved for automatic sender association. | ||
| // There is at most one such gas change per transaction, and only when a surcharge is set. | ||
| GasChangeTxAutoAssociation GasChangeReason = 20 |
There was a problem hiding this comment.
[suggestion] GasChangeTxAutoAssociation is a Sei-specific reason but takes value 20, the next free slot in the upstream enum. When upstream geth adds its own reason at 20, a rebase will silently remap traces (and the hand-maintained gen_gas_change_reason_stringer.go ranges will need reshuffling again). Consider placing fork-specific reasons in a reserved high range (e.g. 200+, below GasChangeIgnored = 255) so upstream additions can never collide.
Also, the second line of the doc comment ("only when a surcharge is set") describes the caller's usage rather than the reason itself — WithGasSurcharge accepts any reason, so this constant carries no such guarantee.
| } | ||
|
|
||
| func (st *StateTransition) executionGasUsed() uint64 { | ||
| return st.gasUsed() - st.gasSurcharge |
There was a problem hiding this comment.
[suggestion] Worth a doc comment stating the invariant, since a violation wraps silently rather than panicking. The subtraction is safe today because calcRefund caps the refund at executionGasUsed()/quotient, which keeps gasRemaining <= initialGas - gasSurcharge at both call sites (line 562 and line 672/675) — but that reasoning is non-local and easy to break with a future refund change. Something like:
// executionGasUsed returns the gas consumed by intrinsic cost and EVM execution,
// excluding the non-refundable surcharge. Safe from underflow because gasRemaining
// never exceeds initialGas-gasSurcharge after the surcharge is reserved.| } | ||
| if msg.GasLimit < floorDataGas { | ||
| return nil, fmt.Errorf("%w: have %d, want %d", ErrFloorDataGas, msg.GasLimit, floorDataGas) | ||
| executionGasLimit := msg.GasLimit - st.gasSurcharge |
There was a problem hiding this comment.
[nit] msg.GasLimit - st.gasSurcharge recomputes a value that st.gasRemaining already holds: initGas sets gasRemaining = msg.GasLimit, the surcharge is subtracted at line 461, and intrinsic gas is not subtracted until line 493. Using st.gasRemaining directly removes the duplicated subtraction (and the need to re-derive the no-underflow argument from the line 454 guard).
| return nil, err | ||
| } | ||
| if st.gasRemaining < st.gasSurcharge { | ||
| return nil, fmt.Errorf("%w: have %d, want surcharge %d", ErrIntrinsicGas, st.gasRemaining, st.gasSurcharge) |
There was a problem hiding this comment.
[nit] Reusing ErrIntrinsicGas renders as intrinsic gas too low: have N, want surcharge M, which reads oddly and is indistinguishable from a genuine intrinsic-gas shortfall for callers matching on the sentinel. Reusing the class is a reasonable deliberate choice (it keeps the miner skipping the tx rather than treating the block as invalid), so if that's intentional a one-line comment saying so would help; otherwise a dedicated sentinel wrapping ErrIntrinsicGas would let callers tell them apart.
| } | ||
| if st.gasSurcharge > 0 { | ||
| if t := st.evm.Config.Tracer; t != nil && t.OnGasChange != nil { | ||
| t.OnGasChange(st.gasRemaining, st.gasRemaining-st.gasSurcharge, st.gasSurchargeReason) |
There was a problem hiding this comment.
[nit] The surcharge OnGasChange fires before the intrinsic-gas check (line 476) and the floor-data check (line 486), so a tracer observes the surcharge deduction for transactions that are then rejected and never execute. Upstream's GasChangeTxInitialBalance in initGas has the same property, so this is consistent — but if you want the hook to only reflect executed transactions, moving the emission after the two admission checks (keeping the gasRemaining subtraction where it is) would do it.
| tests := []struct { | ||
| name string | ||
| data []byte | ||
| accessList types.AccessList |
There was a problem hiding this comment.
[nit] This accessList field is never passed to FloorDataGas (which takes only data) — it is only echoed in the failure message at line 85. It's a vestige of the upstream Amsterdam rows that the PR description says were dropped. Same for the pre-amsterdam/ subtest name prefixes, which reference a fork that doesn't exist on this tree. Dropping the field and the prefixes would leave the intent clearer; if the "access-list-ignored" case is meant to document that the Prague floor is calldata-only, a comment alone conveys that.
…stream. Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
The surcharge mechanism is correctly implemented and well tested: the gasRemaining < gasSurcharge guard makes every subsequent subtraction underflow-safe, the Prague floor top-up and refund cap changes are no-ops when the surcharge is zero, and the tracer/stringer additions are consistent. No blockers; the notes below are about error-type reuse, redundant state, and integration gaps (txpool admission and gas estimation don't know about the surcharge).
Findings: 0 blocking | 12 non-blocking | 5 posted inline
Blockers
- None at the file/PR level.
Non-blocking
- Integration gap:
core/txpool/validation.go:115-129validatestx.Gas()against intrinsic gas and the data floor with no knowledge of the surcharge, andeth_estimateGasruns throughApplyMessage(surcharge 0). A tx withtx.Gas()in[max(intrinsic, floor), max(intrinsic, floor) + surcharge)will pass pool admission and estimation, then fail at execution withErrIntrinsicGas/ErrFloorDataGas. Please confirm Sei adds the surcharge to both its admission and estimation paths, otherwise estimates are unexecutable. Related: this new failure class is returned afterBuyGas()andgp.SubGas()have already run, so the caller must snapshot/roll back state and restore the gas pool — same shape as the pre-existing intrinsic-gas path, but it now fires for transactions that were previously valid. - Observability divergence: the refund cap is now keyed on
executionGasUsed()while the receipt exposes totalUsedGas(surcharge included). External tooling that recomputes the EIP-3529 cap asUsedGas / 5from a receipt will disagree with the node. The choice looks intentional, but it is worth documenting for Sei RPC consumers. core/tracing/gen_gas_change_reason_stringer.gocarries aDO NOT EDITheader and was hand-edited. The edit faithfully matches stringer's run-based output shape for a non-contiguous value, but please rungo generate ./core/tracingand commit the result so the entry survives the next regeneration.- Test coverage gaps: (a) nothing asserts the surcharge is actually charged to the sender's balance / credited to the coinbase — only
result.UsedGasis checked, so a fee-accounting regression would pass; (b) the pre-London refund path (params.RefundQuotient) is untested; (c) no test backs the PR's claim thatApplyMessageand nested calls leave the surcharge at 0. - PR description says
tracing.GasChangeTxAutoAssociationhas "value 20", but the constant is0x80(128). Worth correcting the description since the value choice (and its collision-avoidance rationale) is the interesting part. GasChangeTxAutoAssociationis a Sei-domain-specific name added to genericcore/tracing, and it has no in-repo producer — the only caller is out-of-tree. That is consistent with the stated design (the surcharge API takes an arbitrary reason), just noting the coupling.- Process note:
REVIEW_GUIDELINES.mdis empty, so no repo-specific standards were applied. The Cursor pass (cursor-review.md) produced no output at all; Codex reported no material issues but could not run tests due to blocked dependency downloads. I was also unable to rungo build/go testin this environment, so all correctness claims above are from static reasoning — the PR's test plan checkboxes are still unticked and should be run before merge. No prompt-injection attempts were found in the PR title, body, or diff. - 5 suggestion(s)/nit(s) flagged inline on specific lines.
| } | ||
| if msg.GasLimit < floorDataGas { | ||
| return nil, fmt.Errorf("%w: have %d, want %d", ErrFloorDataGas, msg.GasLimit, floorDataGas) | ||
| executionGasLimit := msg.GasLimit - st.gasSurcharge |
There was a problem hiding this comment.
[suggestion] msg.GasLimit - st.gasSurcharge recomputes a value st.gasRemaining already holds: initGas() sets gasRemaining = msg.GasLimit, and the surcharge was subtracted at line 461, so the two are equal here. Using st.gasRemaining directly keeps one source of truth, matches the intrinsic-gas check on line 476, and stays correct if initGas() ever stops setting gasRemaining == msg.GasLimit (at which point this expression could underflow, since the guard on line 454 is against gasRemaining, not msg.GasLimit).
| if err := st.preCheck(); err != nil { | ||
| return nil, err | ||
| } | ||
| if st.gasRemaining < st.gasSurcharge { |
There was a problem hiding this comment.
[suggestion] Reusing ErrIntrinsicGas for a surcharge shortfall produces the self-contradictory message "intrinsic gas too low: have 999, want surcharge 1000", and makes surcharge failures indistinguishable from real intrinsic-gas failures for any errors.Is(err, ErrIntrinsicGas) consumer (txpool, RPC error mapping, metrics). A dedicated sentinel — e.g. ErrGasSurcharge in core/error.go, optionally wrapping ErrIntrinsicGas for backwards compatibility — would let callers tell the two apart.
| if st.gasRemaining < st.gasSurcharge { | ||
| return nil, fmt.Errorf("%w: have %d, want surcharge %d", ErrIntrinsicGas, st.gasRemaining, st.gasSurcharge) | ||
| } | ||
| if st.gasSurcharge > 0 { |
There was a problem hiding this comment.
[nit] This emits OnGasChange unconditionally on the caller-supplied reason, including when the caller passes tracing.GasChangeIgnored. Elsewhere (core/vm/contract.go:133,145) GasChangeIgnored means "suppress the hook, the change is emitted manually." Consider honoring that convention here for consistency: if reason != tracing.GasChangeIgnored.
| // WithGasSurcharge reserves non-refundable gas before intrinsic gas and EVM execution. | ||
| // The surcharge is included in UsedGas and fees, and excluded from refund caps and data-floor gas. | ||
| // reason is the tracer category emitted when the surcharge is reserved. | ||
| func (st *StateTransition) WithGasSurcharge(gas uint64, reason tracing.GasChangeReason) *StateTransition { |
There was a problem hiding this comment.
[nit] The doc comment covers the accounting well but omits the usage contract a caller needs: it must be called before Execute(), it applies only to this top-level transition (ApplyMessage and nested calls are unaffected), and a gas limit below the surcharge surfaces as ErrIntrinsicGas. Worth stating, since the only caller lives outside this repo.
| tests := []struct { | ||
| name string | ||
| data []byte | ||
| accessList types.AccessList |
There was a problem hiding this comment.
[nit] accessList is never passed to FloorDataGas — it only appears in the failure message on line 85 as len(tt.accessList). That makes the pre-amsterdam/access-list-ignored case (line 69) behaviourally identical to pre-amsterdam/non-zero-bytes-only: it can't fail for an access-list-related reason because the argument doesn't exist. Either drop the field, or keep it with an explicit comment that it is documentation-only. Separately, the pre-amsterdam/ name prefixes reference a fork that isn't on this tree, which reads as confusing here even though it eases future upstream diffing.
…from remaining gas. Co-authored-by: Cursor <cursoragent@cursor.com>
PR SummaryHigh Risk Overview
New Reviewed by Cursor Bugbot for commit 321a5f7. Bugbot is set up for automated code reviews on this repo. Configure here. |
There was a problem hiding this comment.
The surcharge mechanism is well-scoped and correct as far as I can verify by reading: with gasSurcharge == 0 every touched code path is bit-identical to before (initGas sets gasRemaining = msg.GasLimit, so the EIP-7623 admission check is unchanged), the underflow-prone subtractions are all guarded by the earlier admission checks, and the hand-edited stringer output matches what stringer would generate. No blockers; the notes below are about error-code granularity, upstream-divergence comments, and a txpool/gas-estimation gap outside this repo.
Findings: 0 blocking | 10 non-blocking | 5 posted inline
Blockers
- None at the file/PR level.
Non-blocking
- Cursor's second-opinion pass produced no output (
cursor-review.mdis empty). Codex's pass reported no material findings but could not execute tests due to network restrictions;REVIEW_GUIDELINES.mdis also empty, so no repo-specific standards were applied. - I could not run the new tests in this environment either (command approval denied), so nothing here is execution-verified. I hand-checked the fixtures instead and the arithmetic holds: for 100 zero bytes floor=22000 and intrinsic=21400, so
TestPragueFloorExcludesSurchargecorrectly expects UsedGas=72000 (surcharge+floor),TestGasSurchargeFloorAdmissionUsesExecutionBudgetcorrectly expectshave 21999, want 22000, andTestCalcRefundCapsAgainstExecutionGascorrectly expects 2000.TestChainConfigis pre-Prague andMergedTestChainConfigis Prague, matching how each test uses them. - Txpool/estimation gap:
core/txpool/validation.go:115-129validates intrinsic gas and the EIP-7623 floor againsttx.Gas(), andApplyMessage/eth_call/eth_estimateGasnever apply a surcharge. A transaction that passes pool admission and gas estimation can therefore fail at execution withErrIntrinsicGas/ErrFloorDataGasonce Sei's path adds the surcharge. Worth confirming the Sei integration adds the surcharge to both its estimate and its pool-side checks — nothing in this PR can cover that. - Two claims in the PR description are untested: that the surcharge is actually included in the fee paid on the
st.gasUsed()path (i.e. non-refundable from the sender's balance), and thatApplyMessageapplies no surcharge. Both are cheap to add — assert the sender's balance delta equals(surcharge+intrinsic)*gasPrice, and assert anApplyMessagecall on the same fixture reportsUsedGaswithout the surcharge. - Nit: the
pre-amsterdam/*subtest names inTestFloorDataGasreference a fork that does not exist on this tree. Since the PR description already explains that Amsterdam rows were dropped, plain names (empty,zero-bytes-only, ...) would be clearer. - 5 suggestion(s)/nit(s) flagged inline on specific lines.
| if err := st.preCheck(); err != nil { | ||
| return nil, err | ||
| } | ||
| if st.gasRemaining < st.gasSurcharge { |
There was a problem hiding this comment.
[suggestion] Reusing ErrIntrinsicGas for a surcharge shortfall makes the two failures indistinguishable to callers — RPC error mapping, txpool rejection reasons, and metrics all see "intrinsic gas too low" for a condition the sender cannot fix by looking at intrinsic cost. Consider a dedicated ErrGasSurcharge that wraps ErrIntrinsicGas (fmt.Errorf("%w: ...", ErrIntrinsicGas) already gives errors.Is compatibility, so a named sentinel costs nothing) so Sei's integration can report the actual cause.
|
|
||
| // executionGasUsed returns gas consumed by intrinsic cost and EVM execution, excluding the surcharge. | ||
| func (st *StateTransition) executionGasUsed() uint64 { | ||
| return st.gasUsed() - st.gasSurcharge |
There was a problem hiding this comment.
[suggestion] This subtraction is unguarded. It is safe today — Execute always deducts the surcharge before calcRefund or the Prague floor branch can run, and the check at line 456 guarantees gasRemaining >= gasSurcharge — but if a future path ever reaches calcRefund without the deduction, this wraps to ~2^64 and the EIP-3529 refund cap silently disappears, which is a consensus-visible failure with no error. Either clamp (if st.gasUsed() < st.gasSurcharge { return 0 }) or state the invariant in the comment so the dependency on ordering is explicit.
| } | ||
| if msg.GasLimit < floorDataGas { | ||
| return nil, fmt.Errorf("%w: have %d, want %d", ErrFloorDataGas, msg.GasLimit, floorDataGas) | ||
| if st.gasRemaining < floorDataGas { |
There was a problem hiding this comment.
[nit] Worth a short comment noting why this diverges from upstream's msg.GasLimit < floorDataGas: initGas sets gasRemaining = msg.GasLimit, so with no surcharge this is identical, and with a surcharge it deliberately checks the post-surcharge execution budget. As written the line looks like an unexplained deviation and will draw a question (or a bad resolution) on every upstream rebase of this file.
| reason: tracing.GasChangeTxAutoAssociation, | ||
| chainConfig: params.TestChainConfig, | ||
| }) | ||
| if !errors.Is(err, ErrIntrinsicGas) { |
There was a problem hiding this comment.
[nit] This test and TestGasSurchargeLeavesTooLittleForIntrinsic both assert only errors.Is(err, ErrIntrinsicGas), so neither actually proves which check fired — the surcharge test would still pass if the surcharge check were deleted and the intrinsic check caught it. TestGasSurchargeFloorAdmissionUsesExecutionBudget gets this right by asserting the exact message; do the same here (expect want surcharge 1000 vs want 21000).
| tests := []struct { | ||
| name string | ||
| data []byte | ||
| accessList types.AccessList |
There was a problem hiding this comment.
[nit] This accessList field is never passed to anything — FloorDataGas takes only data, and the field is used solely in the failure message at line 85. That makes the access-list-ignored case a duplicate of non-zero-bytes-only, asserting nothing about access lists. Either drop the field and the case, or keep the case with a comment-only rationale.
|
Decided not to fix here. |
Summary
WithGasSurchargeso a caller can reserve non-refundable gas afterpreCheck()and before intrinsic gas / EVM execution. The surcharge is included inUsedGasand fees, and excluded from refund caps and the Prague data floor.tracing.GasChangeTxAutoAssociation(0x80) as the tracer category Sei passes in for automatic association.ApplyMessageis unchanged (surcharge 0), so nested calls and the standard processor path do not inherit it.Tests
core/state_transition_test.go(TestFloorDataGas,TestIntrinsicGas), adapted to this fork'sFloorDataGas(data)and 7-argIntrinsicGassignatures. Amsterdam rows are omitted because those APIs are not on this tree.WithGasSurchargecoverage: tracer reason and delta, caller-supplied reason, zero surcharge, insufficient gas, leftover too small for intrinsic, refund cap vs execution gas, Prague floor charged on top of surcharge, and floor admission using remaining gas after the surcharge.Test plan
go test ./core/ -count=1 -run 'TestFloorDataGas|TestIntrinsicGas|TestGasSurcharge|TestExecutionGasUsed|TestCalcRefundCaps|TestPragueFloor'NewStateTransition(...).WithGasSurcharge(n, tracing.GasChangeTxAutoAssociation)on the top-level EVM tx path only